#include "stdafx.h"
#include "detail/bmock_non_strict_mocks.hpp"

namespace bmock {
	void non_strict_mocks::add_expectation(exp_ptr new_exp) {
		latest_exp_ptr_ = new_exp;
	}

	void non_strict_mocks::expect_call_times_range(int max_, int min_) {
		latest_exp_ptr_->expect_call_times_range(min_, max_);
	}

	void non_strict_mocks::not_received() {
		if (!latest_exp_ptr_->done_minimum_calls()) {
			latest_exp_ptr_->not_received();
		}
	}

	void non_strict_mocks::expect_call(const func_spec& f, const location& loc) {	
		latest_exp_ptr_->expect_call(f, loc);	
	}

	void non_strict_mocks::expect_arguments(const arg_list& args) {	
		latest_exp_ptr_->expect_arguments(args);
		latest_iter_ = insert(std::make_pair(latest_exp_ptr_->get_func_spec().get_loc(), latest_exp_ptr_));
	}

	exp_ptr non_strict_mocks::get_latest() const {
		return latest_exp_ptr_;
	}

	void non_strict_mocks::set_result(const result_ptr& ptr) {
		latest_exp_ptr_->set_result(ptr);
	}

	std::pair<exp_ptr,boost::logic::tribool> non_strict_mocks::check(const func_spec& f, const arg_list& args) {
		std::pair<multimap<location,exp_ptr>::iterator, multimap<location,exp_ptr>::iterator> ret;	
		multimap<location, exp_ptr>::iterator it;

		exp_ptr p_e;
		latest_iter_ = begin();

		ret = equal_range(f.get_loc());	
		for (it = ret.first; it != ret.second; ++it) {		
			latest_iter_ = it;
			try {
				it->second->check_func(f);
				p_e = it->second;
				p_e->check_args_no_msg(args);
			}
			catch (...) {
				continue;
			}
			latest_exp_ptr_ = p_e;
			return std::make_pair(p_e, true);
		}

		if (latest_iter_ != end()) {
			latest_exp_ptr_ = (p_e.get() != NULL) ? p_e : latest_iter_->second;
		}
		if (is_done()) {
			throw std::exception();
		}

		boost::logic::tribool tbool = (p_e.get() != NULL) ? boost::logic::indeterminate : boost::logic::tribool(false);
		return std::make_pair(latest_exp_ptr_, tbool);
	}

	bool non_strict_mocks::is_done() const {
		multimap<location, exp_ptr>::const_iterator it;
		for (it = begin(); it != end(); ++it) {
			if (!it->second->done_minimum_calls()) {
				return false;
			}
		}

		return true;
	}

	bool non_strict_mocks::empty() const {
		return multimap<location,exp_ptr>::empty();
	}

	void non_strict_mocks::pop_last() {
		erase(latest_iter_);
		latest_iter_ = begin();
		if (latest_iter_ != end()) {
			latest_exp_ptr_ = latest_iter_->second;
		}
	}
}